package com.andova.glplugin

import android.content.Context
import android.graphics.BitmapFactory
import android.opengl.GLES30
import android.opengl.GLUtils
import androidx.annotation.RawRes
import com.andova.ext.FLOAT_SIZE_BYTES
import com.andova.ext.getAsset
import com.andova.image.glesInitialize
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer

class GlTexturePlugin(private val context: Context, @RawRes private val textureRawRes: Int) : GlPluginCallback {
    private var texProgram = -1
    private val textureIds = IntArray(1)
    private var vertexBuffer: FloatBuffer? = null
    private var textureBuffer: FloatBuffer? = null
    private fun allocateBufferIfNeeded() {
        if (vertexBuffer != null && textureBuffer != null) return
        val vertices = floatArrayOf(
                -1f, -1f, 1f,
                1f, -1f, 1f,
                1f, 1f, 1f,
                -1f, 1f, 1f
        )
        vertexBuffer = ByteBuffer.allocateDirect(vertices.size * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertices).apply { position(0) }
        val textures = floatArrayOf(
                0f, 1f,
                1f, 1f,
                1f, 0f,
                0f, 0f
        )
        textureBuffer = ByteBuffer.allocateDirect(textures.size * Int.SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer().put(textures).apply { position(0) }
    }

    override fun onPluginCreate(width: Int, height: Int) {
        texProgram = glesInitialize(getAsset(context, "plugins/vert_texture_single.glsl"), getAsset(context, "plugins/frag_texture_single.glsl"))
        GLES30.glGenTextures(textureIds.size, textureIds, 0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0])
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)
        val instr = context.resources.openRawResource(textureRawRes)
        val bmp = try {
            BitmapFactory.decodeStream(instr)
        } catch (e: IOException) {
            null
        } finally {
            try {
                instr.close()
            } catch (t: IOException) {
            }
        }
        bmp ?: return GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE)
        GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bmp, 0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE)

        allocateBufferIfNeeded()
    }

    override fun onPluginChanged(width: Int, height: Int) {
        onPluginDestroy()
        onPluginCreate(width, height)
    }

    override fun onPluginDestroy() {
        vertexBuffer?.clear()
        textureBuffer?.clear()
        vertexBuffer = null
        textureBuffer = null
        GLES30.glDeleteTextures(textureIds.size, textureIds, 0)
    }

    override fun onPluginDraw(x: Int, y: Int, width: Int, height: Int) {
        allocateBufferIfNeeded()
        GLES30.glUseProgram(texProgram)
        GLES30.glViewport(x, y, width, height)
        val attrPos = GLES30.glGetAttribLocation(texProgram, "aPosition")
        val attrTex = GLES30.glGetAttribLocation(texProgram, "aTexCoord")
        GLES30.glVertexAttribPointer(attrPos, 3, GLES30.GL_FLOAT, false, 3 * Int.SIZE_BYTES, vertexBuffer)
        GLES30.glVertexAttribPointer(attrTex, 2, GLES30.GL_FLOAT, false, 2 * Int.SIZE_BYTES, textureBuffer)
        GLES30.glEnableVertexAttribArray(attrPos)
        GLES30.glEnableVertexAttribArray(attrTex)
        GLES30.glEnable(GLES30.GL_BLEND)
        GLES30.glBlendFunc(GLES30.GL_ONE, GLES30.GL_ONE_MINUS_SRC_ALPHA)
//        GLES30.glBlendFunc(GLES30.GL_SRC_ALPHA, GLES30.GL_ONE_MINUS_SRC_ALPHA)
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0])
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4)

        GLES30.glDisable(GLES30.GL_BLEND)
        GLES30.glDisableVertexAttribArray(attrPos)
        GLES30.glDisableVertexAttribArray(attrTex)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE)
    }
}